# -*- coding: utf-8 -*-
"""
    trans_case_csvorexcel_to_xmind.py
    ~~~~~~~~~~~~~~~~~~~~~~~

    将禅道导入格式的csv/xls/xlsx转为xmind，只支持xmind zen


    :author: JoTsai
    :copyright: (c) 2020, Tungee
    :date created: 2020-02-03
    :python version: 3.7
    
    安装依赖：
        pip install openpyxl

    Usage:
        1. 将csv文件导出为xmind：
            python trans_case_csvorexcel_to_xmind.py csv后缀的文件
        
        2. 将xls/xlsx文件导出为xmind：
            python trans_case_csvorexcel_to_xmind.py xls/xlsx后缀的文件


"""
import sys
import csv
import json
import re
import uuid
import zipfile

import openpyxl


def process_case_value(case_table):
    RequiredFields = ["用例标题", "步骤", "预期"]
    # 获取表头
    keys = [str(_).strip() for _ in case_table[0]]
    no_required_fields = [_ for _ in RequiredFields if _ not in keys]
    if no_required_fields:
        print('必须字段不存在：' + str(no_required_fields))
    # 获取列表数据
    case_list = []
    for i, line in enumerate(case_table[1:]):
        values = [str(_).strip() if _ is not None else '' for _ in line]
        case = dict(zip(keys, values))
        for f in RequiredFields:
            if not case.get(f):
                print('第 %d 行的必须字段 %s 为空' % (i + 2, f))
                break
        else:
            case['用例标题'] = re.split('-|:|：', case['用例标题'])
            case["步骤"], case["预期"] = parse_step_expect(case["步骤"], case["预期"])
            case_list.append(case)
    return case_list


def parser_csv_to_case_list(csv_file):
    with open(csv_file, 'r', encoding='gbk') as csvfile:
        reader = csv.reader(csvfile)
        csv_lines = list(reader)
    return process_case_value(csv_lines)


def parser_excel_to_case_list(excel_file):
    wb = openpyxl.load_workbook(excel_file, read_only=True)
    ws = wb[wb.sheetnames[0]]
    cells = ws[ws.calculate_dimension()]
    cells_value = [[c.value for c in r] for r in cells]
    return process_case_value(cells_value)


def parse_step_expect(step_str, expect_str):
    step_list, expect_list = [], []
    step_split_index, expect_split_index = [], []

    remain_step_str, remain_expect_str = step_str, expect_str
    while True:
        index_prefix = str(len(step_split_index) + 1) + '.'
        if index_prefix in remain_step_str:
            remain_index = remain_step_str.find(index_prefix)
            step_split_index.append(
                len(step_str) - len(remain_step_str) + remain_index
            )
            remain_step_str = step_str[step_split_index[-1]:]
            if index_prefix in remain_expect_str:
                remain_index = remain_expect_str.find(index_prefix)
                expect_split_index.append(
                    len(expect_str) - len(remain_expect_str) + remain_index
                )
                remain_expect_str = expect_str[step_split_index[-1]:]
            else:
                expect_split_index.append(expect_split_index[-1])
        else:
            break
    step_split_index.append(len(step_str))
    expect_split_index.append(len(expect_str))
    if len(step_split_index) <= 1:
        print('没有找到步骤的序号，返回空的步骤和预期')
    else:
        for i in range(len(step_split_index) - 1):
            step_list.append(step_str[
                             step_split_index[i] + 2:step_split_index[i + 1]
                             ].strip())
            expect_list.append(expect_str[
                               expect_split_index[i] + 2:expect_split_index[i + 1]
                               ].strip())
    return step_list, expect_list


def xmind_add_child(parent_node, topic):
    if 'children' not in parent_node:
        parent_node['children'] = {}
        parent_node['children']["attached"] = []
    childnode = {'id': str(uuid.uuid1()), 'title': topic}
    parent_node['children']["attached"].append(childnode)
    return childnode


def save_xmindzen_file(content, filename):
    f = zipfile.ZipFile(filename + '.xmind', 'w', zipfile.ZIP_DEFLATED)
    f.writestr(
        'content.json',
        json.dumps(content, ensure_ascii=False))
    f.writestr(
        'manifest.json',
        '{"file-entries":{"content.json":{},"metadata.json":{},"Thumbnails/thumbnail.png":{}}}')
    f.writestr(
        'metadata.json',
        '{"creator":{"name":"Vana","version":"9.3.1.201909210110"},"activeSheetId":"%s"}' % content[0]['id'])
    f.close()


def generate_case_xmindzen(case_list, filename):
    content_json = [{
        "id": uuid.uuid1().hex,
        "class": "sheet",
        "title": filename,
        "rootTopic": {
            "id": uuid.uuid1().hex,
            "class": "topic",
            "title": filename,
            "structureClass": "org.xmind.ui.map.unbalanced",
            "extensions": [{
                "content": [{"content": "2", "name": "right-number"}],
                "provider": "org.xmind.ui.map.unbalanced"
            }]
        },
        "topicPositioning": "fixed"
    }]

    for case in case_list:
        last_node = content_json[0]['rootTopic']
        for topic in case['用例标题']:
            childnodes = last_node.get("children", {}).get("attached", [])
            for child in childnodes:
                if child["title"] == topic:
                    last_node = child
                    break
            else:
                last_node = xmind_add_child(last_node, topic)

        priority_node = xmind_add_child(last_node, case.get('优先级', 3))
        if case.get('关键词'):
            keyword_node = xmind_add_child(priority_node, case['关键词'])
        else:
            keyword_node = xmind_add_child(priority_node, '')
        if case.get('前置条件'):
            precondition_node = xmind_add_child(keyword_node, case['前置条件'])
        else:
            precondition_node = xmind_add_child(keyword_node, '')

        for step, expect in list(zip(case['步骤'], case['预期'])):
            step_node = xmind_add_child(precondition_node, step)
            if expect:
                xmind_add_child(step_node, expect)

    # 保存xmindzen文件
    save_xmindzen_file(content_json, filename)


if __name__ == "__main__":
    global case_list
    if len(sys.argv) < 2:
        print('请输入要转换的测试用例的csv文件或excel文件')
        exit(0)
    case_file = sys.argv[1]
    print(case_file)
    if case_file[-4:] == '.csv':
        case_list = parser_csv_to_case_list(case_file)
    elif case_file[-4:] == '.xls' or case_file[-5:] == '.xlsx':
        case_list = parser_excel_to_case_list(case_file)
    generate_case_xmindzen(case_list, case_file)
    print("已导出xmind文件")
